package com.colin.app.activity.senior.threadpool;

import android.view.View;
import android.widget.Button;

import com.colin.app.R;
import com.colin.app.activity.senior.SeniorContract;
import com.colin.app.activity.senior.SeniorPresenterImpl;
import com.colin.app.app.AppActivity;
import com.colin.app.bean.DemoBean;
import com.colin.library.help.LogHelp;
import com.colin.library.help.TimeHelp;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;


/**
 * 一简介
 * <p/>
 * 线程的使用在java中占有极其重要的地位，在jdk1.4极其之前的jdk版本中，关于线程池的使用是极其简陋的。
 * 在jdk1.5之后这一情况有了很大的改观。Jdk1.5之后加入了java.util.concurrent包，
 * 这个包中主要介绍java中线程以及线程池的使用。为我们在开发中处理线程的问题提供了非常大的帮助。
 * 二：线程池
 * <p/>
 * 线程池的作用：
 * 线程池作用就是限制系统中执行线程的数量。
 * 根据系统的环境情况，可以自动或手动设置线程数量，达到运行的最佳效果；少了浪费了系统资源，多了造成系统拥挤效率不高。用线程池控制线程数量，其他线程排队等候。一个任务执行完毕，再从队列的中取最前面的任务开始执行。若队列中没有等待进程，线程池的这一资源处于等待。当一个新任务需要运行时，如果线程池中有等待的工作线程，就可以开始运行了；否则进入等待队列。
 * 为什么要用线程池:
 * 1.减少了创建和销毁线程的次数，每个工作线程都可以被重复利用，可执行多个任务。
 * 2.可以根据系统的承受能力，调整线程池中工作线线程的数目，防止因为消耗过多的内存，而把服务器累趴下(每个线程需要大约1MB内存，线程开的越多，消耗的内存也就越大，最后死机)。
 * Java里面线程池的顶级接口是Executor，但是严格意义上讲Executor并不是一个线程池，而只是一个执行线程的工具。真正的线程池接口是ExecutorService。
 * <p/>
 * 三：ThreadPoolExecutor详解
 * <p/>
 * ThreadPoolExecutor的完整构造方法的签名是：
 * ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) .
 * <p/>
 * corePoolSize -           池中所保存的线程数，包括空闲线程。
 * maximumPoolSize-         池中允许的最大线程数。
 * keepAliveTime -          当线程数大于核心时，此为终止前多余的空闲线程等待新任务的最长时间。
 * unit - keepAliveTime     参数的时间单位。
 * workQueue -              执行前用于保持任务的队列。此队列仅保持由 execute方法提交的 Runnable任务。
 * threadFactory -          执行程序创建新线程时使用的工厂。
 * handler -                由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序。
 * ThreadPoolExecutor       是Executors类的底层实现。
 */
public class ThreadPoolActivity extends AppActivity implements SeniorContract.SeniorView, View.OnClickListener {
    private SeniorContract.SeniorPresenter mSeniorPresenter = null;
    private Button button_threadpool_scheduled_shutdown;
    private ScheduledExecutorService scheduledThreadPoolExecutor;

    @Override
    protected void onDestroy() {
        if (scheduledThreadPoolExecutor != null && !scheduledThreadPoolExecutor.isShutdown()) {
            scheduledThreadPoolExecutor.shutdown();
            scheduledThreadPoolExecutor = null;
        }
        if (null != mSeniorPresenter) {
            mSeniorPresenter.destroy();
            mSeniorPresenter = null;
        }
        super.onDestroy();
    }

    @Override
    public void initPresenter() {
        DemoBean demoBean = getIntent().getExtras().getParcelable("clazz");
        mSeniorPresenter = new SeniorPresenterImpl(this, this, demoBean);
    }

    @Override
    public int initViewLayoutID() {
        return R.layout.activity_thread_pool;
    }

    @Override
    public void initView() {
        button_threadpool_scheduled_shutdown = (Button) findViewById(R.id.button_threadpool_scheduled_shutdown);
    }

    @Override
    public void initData() {
        mSeniorPresenter.setData();
    }

    @Override
    public void initListener() {
        findViewById(R.id.button_threadpool_fixed).setOnClickListener(this);
        findViewById(R.id.button_threadpool_cached).setOnClickListener(this);
        findViewById(R.id.button_threadpool_scheduled).setOnClickListener(this);
        findViewById(R.id.button_threadpool_single).setOnClickListener(this);
        findViewById(R.id.button_threadpool_custom).setOnClickListener(this);
        button_threadpool_scheduled_shutdown.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.button_threadpool_fixed:
                newFixedThreadPool();
                break;
            case R.id.button_threadpool_cached:
                newCachedThreadPool();
                break;
            case R.id.button_threadpool_scheduled:
                newScheduledThreadPool();
                break;
            case R.id.button_threadpool_scheduled_shutdown:
                if (scheduledThreadPoolExecutor != null && !scheduledThreadPoolExecutor.isShutdown()) {
                    scheduledThreadPoolExecutor.shutdown();
                    scheduledThreadPoolExecutor = null;
                    toastShow("成功关闭scheduled线程池");
                } else {
                    toastShow("还没开启scheduled线程池");
                }
                break;
            case R.id.button_threadpool_single:
                newSingleThreadExecutor();
                break;
            case R.id.button_threadpool_custom:
                newCustomThreadPool();
                break;
        }
    }


    public class MyThread extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                LogHelp.d("MyThread", Thread.currentThread().getName() + ":--" + TimeHelp.getCurrentlyTime() + "-i==" + String.valueOf(i));
            }
        }
    }

    /**
     * FixedThreadPool：通过Executors的newFixedThreadPool方法来创建。
     * 它是一种线程数量固定的线程池，当线程处于空闲状态时，它们并不会被回收，
     * A除非线程池被关闭了。FixedThreadPool中只有核心线程并且这些核心线程没有超时机制，
     * 另外任务队列也是没有大小限制的。
     * <p/>
     * 创建固定大小的线程池。每次提交一个任务就创建一个线程，直到线程达到线程池的最大大小。
     * 线程池的大小一旦达到最大值就会保持不变，如果某个线程因为执行异常而结束，那么线程池会补充一个新线程。
     */
    private void newFixedThreadPool() {
        ExecutorService executorService = Executors.newFixedThreadPool(4);
        //创建实现了Runnable接口对象，Thread对象当然也实现了Runnable接口
        Thread t1 = new MyThread();
        Thread t2 = new MyThread();
        Thread t3 = new MyThread();
        Thread t4 = new MyThread();
        Thread t5 = new MyThread();
        //将线程放入池中进行执行
        executorService.execute(t1);
        executorService.execute(t2);
        executorService.execute(t3);
        executorService.execute(t4);
        executorService.execute(t5);
        //关闭线程池
        executorService.shutdown();
    }

    /**
     * CachedThreadPool：它是一种线程数量不定的线程池，最大线程数可以任意大。
     * 这类线程池比较适合执行大量的耗时较少的任务。当整个线程池处于闲置状态时，线程池中的线程都会超时而被停止，
     * 这个时候CachedThreadPool之中实际是没有任何线程的，它几乎不占用任何系统资源。
     * <p/>
     * 创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程，
     * 那么就会回收部分空闲（60秒不执行任务）的线程，当任务数增加时，此线程池又可以智能的添加新线程来处理任务。
     * 此线程池不会对线程池大小做限制，线程池大小完全依赖于操作系统（或者说JVM）能够创建的最大线程大小。
     */
    private void newCachedThreadPool() {
        ExecutorService pool = Executors.newCachedThreadPool();
        //创建实现了Runnable接口对象，Thread对象当然也实现了Runnable接口
        Thread t1 = new MyThread();
        Thread t2 = new MyThread();
        Thread t3 = new MyThread();
        Thread t4 = new MyThread();
        Thread t5 = new MyThread();
        //将线程放入池中进行执行
        pool.execute(t1);
        pool.execute(t2);
        pool.execute(t3);
        pool.execute(t4);
        pool.execute(t5);
        //关闭线程池
        pool.shutdown();
    }

    /**
     * 3.ScheduledThreadPool：它的核心线程数量是固定的，
     * 而非核心线程数量是没有限制的，并且当非核心线程闲置时会被立即回收。
     * 这类线程池主要用于执行定时任务和具有固定周期的重复任务。
     * <p/>
     * 创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
     */
    private void newScheduledThreadPool() {
//        Runnable runnable = new Runnable() {
//            @Override
//            public void run() {
//
//            }
//        };
//        ScheduledExecutorService executorService = Executors.newScheduledThreadPool(4);
//        executorService.schedule(runnable, 2000, TimeUnit.MILLISECONDS);
//        executorService.scheduleAtFixedRate(runnable, 10, 1000, TimeUnit.MILLISECONDS);

        if (scheduledThreadPoolExecutor == null) {
            scheduledThreadPoolExecutor = Executors.newScheduledThreadPool(4);
            scheduledThreadPoolExecutor.scheduleAtFixedRate(new Runnable() {//每隔一段时间就触发异常
                @Override
                public void run() {
                    //throw new RuntimeException();
                    LogHelp.d("每隔一段时间就触发异常");
                }
            }, 1000, 5000, TimeUnit.MILLISECONDS);
            scheduledThreadPoolExecutor.scheduleAtFixedRate(new Runnable() {//每隔一段时间打印系统时间，证明两者是互不影响的
                @Override
                public void run() {
                    LogHelp.d(TimeHelp.getCurrentlyTime());
                }
            }, 1000, 2000, TimeUnit.MILLISECONDS);
        }

    }


    /**
     * 4、SingleThreadExecutor：它的线程池内部只有一个核心线程，它确保所有的任务都在同一个线程中按顺序执行。
     * 其意义在于统一所有的外界任务到一个线程中，使得在这些任务之间不需要处理线程同步的问题。
     * <p/>
     * 创建一个单线程的线程池。这个线程池只有一个线程在工作，也就是相当于单线程串行执行所有任务。
     * 如果这个唯一的线程因为异常结束，那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
     */
    private void newSingleThreadExecutor() {
        //创建一个可重用固定线程数的线程池
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        //创建实现了Runnable接口对象，Thread对象当然也实现了Runnable接口
        Thread t1 = new MyThread();
        Thread t2 = new MyThread();
        Thread t3 = new MyThread();
        Thread t4 = new MyThread();
        Thread t5 = new MyThread();
        //将线程放入池中进行执行
        executorService.execute(t1);
        executorService.execute(t2);
        executorService.execute(t3);
        executorService.execute(t4);
        executorService.execute(t5);
        //关闭线程池
        executorService.shutdown();
    }

    private void newCustomThreadPool() {
        //创建一个有个5工作线程的线程池
        MyThreadPool threadPool = new MyThreadPool(5);
        try {
            //休眠1秒,以便让线程池中的工作线程全部运行
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (int i = 0; i < 7; i++) {  //创建7个任务
            threadPool.execute(createTask(i));
        }
        //等待所有任务执行完毕
        threadPool.waitFinish();
        //关闭线程池
        threadPool.closePool();
    }


    private static Runnable createTask(final int taskID) {
        return new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    LogHelp.d("自定义线程池", Thread.currentThread().getName() + ":--" + TimeHelp.getCurrentlyTime() + "-i==" + String.valueOf(i));
                }
            }
        };
    }


}
